home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume90 / unix / ls_4_0k / part04 < prev   
Encoding:
Internet Message Format  |  1990-07-03  |  47.6 KB

  1. Path: xanth!cs.odu.edu!Amiga-Request
  2. From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v90i198: ls 4.0k - yet another unix-like ls command, Part04/04
  5. Message-ID: <13042@xanth.cs.odu.edu>
  6. Date: 3 Jul 90 19:26:07 GMT
  7. Sender: tadguy@cs.odu.edu
  8. Reply-To: kim@uts.amdahl.com (Kim E. DeVaughn)
  9. Lines: 1546
  10. Approved: tadguy@cs.odu.edu (Tad Guy)
  11. X-Mail-Submissions-To: Amiga@cs.odu.edu
  12. X-Post-Discussions-To: comp.sys.amiga
  13.  
  14. Submitted-by: kim@uts.amdahl.com (Kim E. DeVaughn)
  15. Posting-number: Volume 90, Issue 198
  16. Archive-name: unix/ls-4.0k/part04
  17.  
  18. #!/bin/sh
  19. # This is a shell archive.  Remove anything before this line, then unpack
  20. # it by saving it into a file and typing "sh file".  To overwrite existing
  21. # files, type "sh file -c".  You can also feed this as standard input via
  22. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  23. # will see the following message at the end:
  24. #        "End of archive 4 (of 4)."
  25. # Contents:  src/ls.c.aa
  26. # Wrapped by tadguy@xanth on Tue Jul  3 15:22:14 1990
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. if test -f 'src/ls.c.aa' -a "${1}" != "-c" ; then 
  29.   echo shar: Will not clobber existing file \"'src/ls.c.aa'\"
  30. else
  31. echo shar: Extracting \"'src/ls.c.aa'\" \(45212 characters\)
  32. sed "s/^X//" >'src/ls.c.aa' <<'END_OF_FILE'
  33. X/* ----------------------------------------------------------------------
  34. X   ls.c  -  main source code file for the "improved" directory utility.
  35. X
  36. Xls v4.0k is (c) Copyright 1990, Kim E. DeVaughn, all rights reserved.
  37. X
  38. X
  39. XFYI:
  40. X
  41. X  UNIX is a registered trademark of AT&T.  They had nothing to do with any
  42. X  of this code, but did provide the "model" for some of its output formats
  43. X  and options.
  44. X
  45. X
  46. XAuthor:
  47. X
  48. X  Kim E. DeVaughn
  49. X
  50. X  v4.0k  May 11, 1990  Pretty much completely revised the output formats.
  51. X               Much more like UNIX's "ls" in its output, options,
  52. X               etc.  Several bugs fixed.  Many new options.
  53. X
  54. X  Justin V. McCormick
  55. X
  56. X  v3.1    July 29, 1989  Bug fixes, new output width and height options.
  57. X  v3.0    July 25, 1989  Instant sorting, best-fit output, new features.
  58. X  v2.2    December 1988  Fixed status return and multiple wildcard pathnames.
  59. X  v2.1    December 1988  Minor size reduction, fixed a few bugs from 2.0.
  60. X  v2.0    November 1988  Revised for Lattice 5.0 and made 1.3 compatible.
  61. X  v1.0      August 1986  Written from scratch, my first Amiga project.
  62. X
  63. X
  64. XNotice:
  65. X
  66. XThis program is copyrighted.  See the "License" accompanying this distribution
  67. Xfor limitations, and other details.
  68. X
  69. X
  70. XSynopsis:
  71. X
  72. XFeatures intelligent columnar listing, versatile sort options, UNIX-style
  73. Xpattern matching, UNIX-like output formats and flags, recursive subdirectory
  74. Xlisting, and more!
  75. X
  76. X
  77. XUsage:
  78. X    ls [ [ -options <args> ] [ names ] ] ...
  79. X
  80. X
  81. XBugs and Limitations:
  82. X
  83. X"ls" cannot pattern match devices (like "dh*:"), since this would involve
  84. Xanother level of recursion and parsing the Device List.  See the README file
  85. Xfor other limitations, and known bugs.
  86. X
  87. X
  88. XHistory:
  89. X
  90. X  Changes From 3.1 to 4.0k:
  91. X
  92. X  o  New (default) long listing format that *looks* like UNIX's (except for
  93. X     things like owner, group ID, etc).
  94. X  o  Block sizes now include *all* blocks associated with a file, and are
  95. X     finally accurate for FFS devices.
  96. X  o  By default, upper/lower case *is* significant in the output listing,
  97. X     and in the wildcard pattern matching.
  98. X  o  By default, directories and files are intermixed, according to their
  99. X     alphabetic position.
  100. X  o  By default, the short listing format uses fixed width columns, based
  101. X     on the length of the longest filename in the directory.  Variable
  102. X     width columns are still available.
  103. X  o  By default, "hidden", "*.info", and "dot" files (eg, .foo) are not
  104. X     displayed.  There are switches for each, as well as an "all" flag.
  105. X  o  Several new options like the ability to limit the "-R" recursion depth;
  106. X     ability to show absolute and relative path names in the long format
  107. X     listing;  additional control of directory header and totalization
  108. X     lines;  elimination of redundent totalization lines;  showing/sorting
  109. X     on files' "disk keys";  control of the date format;  sort by date/size
  110. X     defaults to newest/biggest first;    and a few other things.
  111. X  o  Improved error handling and error msgs.  Better "break" handling.  No
  112. X     more "memory leaks" (yes, there is one in v3.1 ... it looses 300 bytes
  113. X     on an error exit).
  114. X  o  The assignment of option flags has been "rationalized".  Applicable
  115. X     flags from the UNIX "ls" were assigned 1st, followed by other flags
  116. X     from v3.1, followed by new flags added in this rev.  However ... some
  117. X     flag assignments from previous revs were changed when better mnemonic
  118. X     values could be found, etc.   So ... check the usage, and/or docs.
  119. X     Caveat emptor!
  120. X  o  Flags requiring arguments (eg, -N) may or may not be seperated from
  121. X     the argument by spaces.  So, "-Nfoo" is just as legal as "-N  foo".
  122. X
  123. X
  124. X  Changes From 3.0 to 3.1:
  125. X
  126. X  o Fixed random errors from uninitialized argument count in GetCLIArgs().
  127. X  o New -X and -Y options to specify short format columns and rows.
  128. X
  129. X
  130. X  Changes From 2.2 to 3.0:
  131. X
  132. X  o Added many new command line options!  See Usage descriptions and docs.
  133. X  o "Instant" output sorting, now using an on-the-fly insertion sort.
  134. X  o New Short listing technique is row-by-row, redirects to PRT: or file.
  135. X  o Better columnization of short listings, uses best-fit optimization.
  136. X  o Formatted output parsing system implemented for long listings.
  137. X  o Improved ^C handling, now breaks immediately in mid-output.
  138. X  o New command line parser, now handles multiple options mixed with names.
  139. X  o Added separate pattern matching for directories and files. [ DOESN'T WORK - KED ]
  140. X  o Now inhibits system requesters from popping up by default.
  141. X  o Added custom cres.o module for more size savings.
  142. X  o Now uses Exec List functions for smaller, faster code.
  143. X  o Fixed random cli_ReturnCode and pr_Result2 value CLI returns.
  144. X  o Rewrote many sections, further code cleaning/commenting.
  145. X  o Eliminated main(), LS handles the _main() function for smaller code.
  146. X  o Compiled with 5.03.90 (beta) Lattice compiler, saved a few bytes.
  147. X
  148. X
  149. X  Changes From 2.1 to 2.2:
  150. X
  151. X  o Fixed erroneous Status returns.
  152. X  o Fixed bug with multiple wildcarded pathnames.
  153. X  o Compiled with LC 5.0 Patch 3 and CAPE 2.0, saved another 46 bytes.
  154. X  o Eliminated an extra stpcpy(), saved another few bytes.
  155. X
  156. X
  157. X  Changes From 2.0 to 2.1:
  158. X
  159. X  o Fixed the show comment feature, a last minute bug in 2.0.
  160. X  o Fixed the "Unknown option ''" message problem.
  161. X  o Optimized the assembly branches, reduced code size another few bytes.
  162. X
  163. X
  164. X  Changes From 1.0 to 2.0:
  165. X
  166. X  o Source code prototyped, linted, traced, optimized, tweaked, etc.
  167. X  o Made resident ("pseudo-pure") by linking with cres.o from LC 5.0.
  168. X  o High-volume routines recoded in assembly (lssup.a).
  169. X  o Now handles multiple paths/files on a command line, up to 30.
  170. X  o New sort flags, including no sort.
  171. X  o Enhanced wildcards, understands complex *.?*.* expressions now.
  172. X  o More efficient ExNext() performance, less ram used for recursion.
  173. X  o SIGBREAKF_CTRL_C signal (Ctrl-C) cleanly aborts at any point now.
  174. X  o Command line parser handles quoted pathnames now (LC 5.0 benefit).
  175. X  o Short listing finally auto-adjusts to new console window sizes!
  176. X  o Pen color escape codes bypassed when redirecting long output.
  177. X  o Sorting by size or date is also subsorted alphabetically now.
  178. X  o Long listing shows new 1.3 file attributes, plus comment indicator.
  179. X  o File dates are now in international format, YY-MM-DD.
  180. X  o Fixed listings with files datestamped after 99-12-31 (overflow).
  181. X  o Fixed listings with files datestamped before 78-01-01 (time < 0).
  182. X  -------------------------------------------------------------------- */
  183. X
  184. X/* ---------------------- LS SOURCE ---------------------------------- */
  185. X
  186. X#include "ls.h"
  187. X
  188. X/* Extern CODE from lssup.a */
  189. Xextern BYTE *aindex __ARGS((BYTE *, BYTE));
  190. Xextern LONG  __stdargs asprintf __ARGS((BYTE *, BYTE *,...));
  191. Xextern LONG CompareDateStamps __ARGS((struct DateStamp *, struct DateStamp *));
  192. Xextern LONG CompFibs __ARGS((LONG, struct FileInfoBlock *, struct FileInfoBlock *));
  193. Xextern LONG FillFibEntry __ARGS((struct List *, struct FileInfoBlock *));
  194. Xextern LONG GetFileString __ARGS((BYTE *, BYTE *));
  195. Xextern LONG GetPathString __ARGS((BYTE *, BYTE *));
  196. Xextern LONG iswild __ARGS((BYTE *));
  197. Xextern LONG wildmatch __ARGS((BYTE *, BYTE *));
  198. Xextern VOID *myalloc __ARGS((LONG));
  199. Xextern VOID FibFileDate __ARGS((struct DateStamp *, BYTE *, BYTE *));
  200. Xextern VOID GetWinBounds __ARGS((LONG *, LONG *));
  201. Xextern VOID InsertFibNode __ARGS((struct List *, struct FibEntry *));
  202. Xextern VOID MakePathString __ARGS((struct FileLock *, BYTE *));
  203. Xextern VOID myfree __ARGS((VOID *));
  204. X
  205. X/* Local DATA */
  206. Xstruct DateStamp theolddate;     /* Show files older than this date */
  207. Xstruct DateStamp thenewdate;     /* Show files newer than this date */
  208. Xstruct FileHandle *ConOut;     /* Standard output */
  209. Xstruct FileHandle *ConIn;     /* Standard input */
  210. Xstruct FileLock *CurFLock   = 0; /* Global Directory lock */
  211. Xstruct FileInfoBlock *GFibp = 0; /* Global FIB for Examine/ExNext */
  212. Xstruct InfoData *CurID        = 0; /* Global InfoData for Info */
  213. XAPTR   OldWindowPtr;         /* Copy of what was in pr_WindowPtr */
  214. X
  215. X/* User flags (see ls.h for bit definitions) */
  216. XULONG LSFlags  = SHOWDIRS | SHOWFILES;
  217. XULONG LSFlagsX = 0;
  218. XULONG oldsig   = 0;
  219. X
  220. XLONG gentrycnt;         /* Grand klugde used to control printing of gtotals */
  221. XLONG gdircount;         /* Grand total # of dirs for Recursive */
  222. XLONG gfilecount;        /* Grand total # of files found */
  223. XLONG gitemcnt;            /* Grand kludge, the sequel ... for printing BS */
  224. XLONG gtotblocks;        /* Grand total # of blocks */
  225. XLONG gtotbytes;         /* Grand total # of bytes */
  226. X
  227. XLONG dircount;            /* Number of directories found */
  228. XLONG filecount;         /* Number of files found */
  229. XLONG totblocks;         /* total # of blocks */
  230. XLONG totbytes;            /* total # of bytes */
  231. XLONG maxnamlen;         /* Longest name found */
  232. XLONG sortkey;            /* 0=alpha, 1=size, 2=date, 3=diskkey */
  233. XLONG datefmt = AGE_TO_YEARS;    /* format for date in new long listing */
  234. XLONG recurlevel = 0;
  235. XLONG recurlimit = MAXDEPTH;
  236. XLONG ioerrcode    = 0;
  237. XLONG blksize    = 0;
  238. X
  239. XBYTE padtab[PADTABSIZE];    /* Column table tab amounts */
  240. XBYTE initialpath[WORKSIZE];    /* Path where we were when started */
  241. XBYTE thePath[WORKSIZE];     /* Current filename space */
  242. XBYTE theDirPat[WORKSIZE];    /* Dirname pattern workspace */
  243. XBYTE theFilePat[WORKSIZE];    /* Filename pattern workspace */
  244. XBYTE workstr[WORKSIZE+64];    /* Temp string space */
  245. X
  246. XBYTE theblksstr[20];
  247. XBYTE thedatestr[12];
  248. XBYTE theprotstr[12];
  249. XBYTE thesizestr[20];
  250. XBYTE thetimestr[12];
  251. X
  252. XLONG CurWinRows = 0;        /* Window bounds, determined at runtime */
  253. XLONG CurWinCols = 0;
  254. X
  255. X/* Initialized Strings */
  256. XBYTE Author[]  = "\23333mls v4.0k\2330m  \251 Copyright  Kim E. DeVaughn  05/11/90  [non-k revs: J. McCormick]\n";
  257. XBYTE usage[]   = "usage: ls [ [-options <args>]  [names] ] ...\n";
  258. XBYTE usage0[]  = "  a  Show dot files       s  Sort by size           M  Ignore case w/wildcard\n";
  259. XBYTE usage1[]  = "  b  Show data blks       t  Sort by date           N <name> Show newer than\n";
  260. XBYTE usage2[]  = "  c  Show filenotes       u  Usage  [also -?]       O <name> Show older than\n";
  261. XBYTE usage3[]  = "  d  Show dirs only       v  Vari col short list    P  Show full pathnames\n";
  262. XBYTE usage4[]  = "  e  Execute bit is \"e\"   x <pat> Exclude files     Q  Requesters enabled\n";
  263. XBYTE usage5[]  = "  f  Show files only      z  Override blk calc      R  Recursive listing\n";
  264. XBYTE usage6[]  = "  h  Show hidden files    A  Show all  [= -ahi]     S  Show dirs first\n";
  265. XBYTE usage7[]  = "  i  Show *.info files    B <blk> Force blk size    T  Totals for all entries\n";
  266. XBYTE usage8[]  = "  k  Sort by disk key     C  Single column list     V  Show rel pathnames\n";
  267. XBYTE usage9[]  = "  l  Long listing         D  Show dirs last         W  No contents (wild dir)\n";
  268. XBYTE usage10[] = "  m  Mixed case output    E  No ANSI escape codes   X <wide> Set output cols\n";
  269. XBYTE usage11[] = "  n  No sort              G  No subdir totals       Y <high> Set output rows\n";
  270. XBYTE usage12[] = "  o  Old long list fmt    H  No headings            Z  Force ANSI sequences\n";
  271. XBYTE usage13[] = "  p  Append \"/\" to dirs   I  No page prompts       0-6 Date format (new long)\n";
  272. XBYTE usage14[] = "  q  Quit on not found    K  Show disk keys         -  Next arg is filename\n";
  273. XBYTE usage15[] = "  r  Reverse sort         L <n> Limited recursion\n";
  274. XBYTE usage16[] = "  F <format> Format output [-o fmt] (default: \"";
  275. XBYTE usage17[] = "\")\n";
  276. X
  277. X/* Date and output format strings */
  278. XBYTE baddatestr[]     = "00-00-00";
  279. XBYTE badtimestr[]     = "00:00:00";
  280. XBYTE datepat[]          = "%02ld-%02ld-%02ld";
  281. XBYTE timepat[]          = "%02ld:%02ld:%02ld";
  282. XBYTE dayspermonth[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
  283. XBYTE deffmtstr[40]    = "%p %d %t %4b %8s %n\\n";
  284. XBYTE LongFmtStr[]     = "%ld";
  285. XBYTE totalfmtstr[]    = "Dirs: %-4ld Files: %-5ld Blocks: %-5ld Bytes: %-8ld\n";
  286. XBYTE filefmtstr[]     = "           Files: %-5ld Blocks: %-5ld Bytes: %-8ld\n";
  287. XBYTE TotHeaderMsg[]   = "\nTotals:\n";
  288. XBYTE ColonStr[]       = ":";
  289. XBYTE SlashStr[]       = "/";
  290. XBYTE RamNameStr[]     = "RAM:";
  291. Xchar *months[] = {"","Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"};
  292. Xchar exbit = 'x';
  293. X
  294. X/* ANSI color control strings */
  295. XBYTE penstr0[]          = "\2330m";       /* Reset CON:   */
  296. XBYTE penstr1[]          = "\2330 p";      /* Cursor off   */
  297. XBYTE penstr2[]          = "\233 p";       /* Cursor on    */
  298. XBYTE penstr3[]          = "\23333m";      /* Color 3 NORM */
  299. XBYTE penstr4[]          = "\2331;33;40m"; /* Color 3 BOLD */
  300. XBYTE penstr5[]          = "\2331;31;40m"; /* Color 1 BOLD */
  301. XBYTE penstr6[]          = "\2337;33m\2330 p"; /* Color 3 INV w/cursor off  */
  302. X
  303. X/* CON: command sequence for window bounds report */
  304. XBYTE gwbrstr[4] = {  0x9b, '0', ' ', 'q' };
  305. X
  306. X/* Newline and "" source */
  307. X#define NULLSTR   &NLine[1]
  308. XBYTE    NLine[4]  = {10, 0, 0, 0};
  309. XBYTE    badopt[4] =  {0, 0, 0, 0};
  310. X
  311. X/* Pointers to usage strings for quick easy output */
  312. XBYTE *usagestrs[] =
  313. X{
  314. X  Author,  usage,   usage0,  usage1,    usage2,  usage3,  usage4,  usage5,
  315. X  usage6,  usage7,  usage8,  usage9,    usage10, usage11, usage12, usage13,
  316. X  usage14, usage15, usage16, deffmtstr, usage17,
  317. X  0L
  318. X};
  319. X
  320. XBYTE *thefmtstr = deffmtstr;    /* Format string pointer for long output */
  321. XBYTE *curpath;
  322. XBYTE *theAntiPat;        /* Avoid pattern string */
  323. X
  324. X/* Local CODE */
  325. XBYTE *GetDecNum __ARGS((BYTE *, LONG *));
  326. XLONG CmpDateBounds __ARGS((struct DateStamp *));
  327. XLONG GetFileDate __ARGS((BYTE *, struct DateStamp *));
  328. XLONG ParseCmdOptions __ARGS((LONG, LONG, BYTE **));
  329. Xstruct FibEntry *AllocFib __ARGS((VOID));
  330. Xstruct FibEntry *ModNextFib __ARGS((struct FibEntry *, LONG));
  331. Xstruct List *GetDir __ARGS((struct FileLock *, struct FileInfoBlock *));
  332. XVOID DirIt __ARGS((struct FileLock *, BYTE *));
  333. XVOID FreeAllFibs __ARGS((struct List *));
  334. XVOID GetCLIArgs __ARGS((BYTE *, LONG *, BYTE **));
  335. XVOID LListDir __ARGS((struct List *));
  336. XVOID LListEntry __ARGS((struct FileInfoBlock *));
  337. XVOID LongList __ARGS((LONG *, LONG *, struct List *));
  338. XVOID PagePrompt __ARGS((LONG, LONG));
  339. XVOID ParseFormatOpts __ARGS((struct FileInfoBlock *));
  340. XVOID SetConPen __ARGS((BYTE *));
  341. XVOID SListDir __ARGS((struct List *));
  342. XVOID TestBreak __ARGS((VOID));
  343. XVOID Usage __ARGS((LONG));
  344. XVOID WCHR __ARGS((BYTE *));
  345. XVOID WSTR __ARGS((BYTE *));
  346. XVOID _main __ARGS((BYTE *));
  347. XLONG attrstr __ARGS((register BYTE *, register LONG *, register LONG *));
  348. XVOID fixNumBlocks __ARGS((struct FileLock *, FIB *));
  349. XLONG blkalloc __ARGS((register FIB *));
  350. XLONG datestr __ARGS((BYTE *, struct DateStamp *, LONG));
  351. XVOID cleanup __ARGS((LONG, LONG));
  352. XVOID errmsg __ARGS((BYTE *, LONG));
  353. XVOID llistentry __ARGS((register FIB *));
  354. X
  355. X
  356. X/*
  357. X *  cleanup() - Unlock any locks we may have, free up allocations, turn the
  358. X *        system requesters back on, and get on out ...
  359. X *
  360. X */
  361. X
  362. XVOID cleanup(result2, retcode)
  363. X  LONG result2;
  364. X  LONG retcode;
  365. X{
  366. X  struct Process *procp;
  367. X
  368. X/* Make sure we unlock any locks we created! */
  369. X  if ((CurFLock != 0) && ((LSFlags & PATHNAMED) != 0))
  370. X    UnLock((BPTR)CurFLock);
  371. X
  372. X/* Free our fib and id */
  373. X  if (GFibp != 0) myfree(GFibp);
  374. X  if (CurID != 0) myfree(CurID);
  375. X
  376. X/* Put windowptr back */
  377. X  procp = (struct Process *)FindTask(0L);
  378. X  procp->pr_WindowPtr = OldWindowPtr;
  379. X
  380. X/* Set return status, exit */
  381. X  procp->pr_Result2 = result2;
  382. X  exit((int)retcode);
  383. X}
  384. X
  385. X
  386. X/*
  387. X *  errmsg() - Print meaningful error messages when something screws up.
  388. X *
  389. X */
  390. X
  391. XVOID errmsg(fn, errcode)
  392. X  BYTE *fn;
  393. X  LONG    errcode;
  394. X{
  395. X  LONG len;
  396. X
  397. X/* Make sure we unlock any locks we created! */
  398. X  if ((CurFLock != 0) && ((LSFlags & PATHNAMED) != 0))
  399. X  {
  400. X    UnLock((BPTR)CurFLock);
  401. X    CurFLock = 0;
  402. X  }
  403. X
  404. X  if (errcode == -1L) errcode = ioerrcode = IoErr();
  405. X
  406. X  len = strcspn(fn, ":") + 1;
  407. X  strncpy(workstr, fn, len);
  408. X  *(workstr + len) = '\0';
  409. X
  410. X  switch(errcode)
  411. X  {
  412. X    case ERROR_NO_FREE_STORE:
  413. X      WSTR("ls: Out of memory\n");
  414. X      cleanup(errcode, 20L);
  415. X    case ERROR_OBJECT_IN_USE:
  416. X      asprintf(workstr, "%s: File or directory in use\n", fn);
  417. X      break;
  418. X    case ERROR_DIR_NOT_FOUND:
  419. X    case ERROR_OBJECT_NOT_FOUND:
  420. X    case ERROR_OBJECT_WRONG_TYPE:
  421. X      asprintf(workstr, "%s: No such file or directory\n", fn);
  422. X      break;
  423. X    case ERROR_DISK_NOT_VALIDATED:
  424. X      strcat(workstr, " Filesystem not validated\n");
  425. X      break;
  426. X    case ERROR_DEVICE_NOT_MOUNTED:
  427. X      strcat(workstr, " Device not mounted or assigned\n");
  428. X      break;
  429. X    case ERROR_NOT_A_DOS_DISK:
  430. X      strcat(workstr, " Invalid filesystem format [non-DOS]\n");
  431. X      break;
  432. X    case ERROR_NO_DISK:
  433. X      strcat(workstr, " Device empty\n");
  434. X      break;
  435. X    case PATTERN_ERR:
  436. X      WSTR("ls: Directory or filename pattern too long\n");
  437. X      return;
  438. X    case WILDSPEC_ERR:
  439. X      asprintf(workstr, "%s: Unable to pattern match paths\n", fn);
  440. X      break;
  441. X    default:
  442. X      asprintf(workstr, "ls: %s: Error - %ld\n", fn, errcode);
  443. X      WSTR(workstr);
  444. X      cleanup(errcode, 20L);
  445. X  }
  446. X  WSTR(workstr);
  447. X}
  448. X
  449. X
  450. X/*
  451. X *  datestr() - Converts an AmigaDOS "datestamp" into an ASCII string in one
  452. X *        of several defined formats.
  453. X *
  454. X *        A non-zero return indicates an unknown format was requested,
  455. X *        in which case the return string is null'd.
  456. X *
  457. X */
  458. X
  459. XLONG datestr(s, ds, format)
  460. X  BYTE *s;
  461. X  struct DateStamp *ds;
  462. X  LONG format;
  463. X{
  464. X    register LONG t, mo, yr;
  465. X    LONG y2, day, hr, min, sec;
  466. X    struct  DateStamp  cds;
  467. X
  468. X    t     =  ds->ds_Days - 2251;   /* This date algorithm by Tom Rokicki.    */
  469. X    yr     =  (4 * t + 3) / 1461;   /* It was taken from Rob Peck's book:     */
  470. X    t    -=  1461 * yr / 4;      /*   "Programmer's Guide to the Amiga",   */
  471. X    yr    +=  1984;          /* and is supposed to be good until 2100; */
  472. X    mo     =  (5 * t + 2) / 153;    /* doesn't work for dates before 03/01/84 */
  473. X    day  =  t - ((153 * mo + 2) / 5) + 1;
  474. X    mo    +=  3;
  475. X    if (mo > 12) {
  476. X    yr++;
  477. X    mo -= 12;
  478. X    }
  479. X    y2 = (yr < 2000) ? yr-1900 : yr-2000;       /* after 2099 ... punt! */
  480. X
  481. X
  482. X    t    =  ds->ds_Minute * 60 + ds->ds_Tick/TICKS_PER_SECOND;
  483. X    sec =  t % 60; t /= 60;
  484. X    min =  t % 60; t /= 60;
  485. X    hr    =  t % 24;
  486. X
  487. X
  488. X    switch (format) {
  489. X
  490. X    case AGE_TO_YEARS :
  491. X     DateStamp(&cds);
  492. X     if (((cds.ds_Days - ds->ds_Days) > TIME_YEAR_THRESHOLD) ||
  493. X         ((cds.ds_Days - ds->ds_Days) < 0)) {
  494. X         asprintf(s, "%s %2ld %5ld\0", months[mo], day, yr);
  495. X     } else {
  496. X         asprintf(s, "%s %2ld %02ld:%02ld\0", months[mo], day, hr, min);
  497. X     }
  498. X     break;
  499. X
  500. X    case FULL_FORM :
  501. X     asprintf(s, "%s %02ld %4ld  %02ld:%02ld:%02ld\0", months[mo], day, yr, hr, min, sec);
  502. X     break;
  503. X
  504. X    case DASHA_FORM :
  505. X     asprintf(s, "%02ld-%s-%02ld %02ld:%02ld:%02ld\0", day, months[mo], y2, hr, min, sec);
  506. X     break;
  507. X
  508. X    case DASHN_FORM :
  509. X     asprintf(s, "%02ld-%02ld-%02ld %02ld:%02ld:%02ld\0", mo, day, y2, hr, min, sec);
  510. X     break;
  511. X
  512. X    case SLASH_FORM :
  513. X     asprintf(s, "%02ld/%02ld/%02ld %02ld:%02ld:%02ld\0", mo, day, y2, hr, min, sec);
  514. X     break;
  515. X
  516. X    case EURO_FORM :
  517. X     asprintf(s, "%02ld/%02ld/%02ld %02ld:%02ld:%02ld\0", day, mo, y2, hr, min, sec);
  518. X     break;
  519. X
  520. X    case DOT_FORM :
  521. X     asprintf(s, "%02ld.%02ld.%02ld %02ld:%02ld:%02ld\0", y2, mo, day, hr, min, sec);
  522. X     break;
  523. X
  524. X    default :
  525. X     *s = '\0';     /* bad format ... null out the filedate string */
  526. X     return(1);
  527. X    }
  528. X    return(0);
  529. X}
  530. X
  531. X
  532. X/*
  533. X *  attrstr() - Converts FileInfoBlock DirEntryType & Protection entries into
  534. X *        an ASCII string representation.  Format is:
  535. X *
  536. X *            dhsparwxd    ["h" may possibly be replaced by "H" or "+"]
  537. X *
  538. X *        where a letter is replaced by a "-" if the corresponding
  539. X *        attribute is not associated with the current FIB entry (i.e.,
  540. X *        if the bit is not "true").  [The "H" or "+" form indicates
  541. X *        one or more of the currently reserved/undefined high-order
  542. X *        bits (as of 1.3) are on.  An "H" is used if the "hidden" bit
  543. X *        is also on ... a "+" is used if it is not.]
  544. X *
  545. X *        A non-zero return code indicates the "H" or "+" form was
  546. X *        detected.
  547. X *
  548. X *        Note: Due to historical braindamage in AmigaDOS, bits 0-3
  549. X *              (the original "protection" bits) are "active low",
  550. X *              while bits 4-7 (more properly called "attribute" bits)
  551. X *              are "active high".  At least this is based on the way
  552. X *              the 1.3 Delete, Resident, Shell, and Protect commands
  553. X *              interpret the "d", "p", and "s" bits (and how the List
  554. X *              command displays them).  [ Thanks alot Tim ...! ]
  555. X *
  556. X */
  557. X
  558. XLONG attrstr(s, type, prot)
  559. X  register BYTE *s;
  560. X  register LONG *type;
  561. X  register LONG *prot;
  562. X{
  563. X    BYTE *t;
  564. X
  565. X       t = s;
  566. X    *s++ = (*type > 0)              ? 'd' : '-';
  567. X    *s++ = (*prot & FIBF_HIDDEN)    ? 'h' : '-';
  568. X    *s++ = (*prot & FIBF_SCRIPT)    ? 's' : '-';
  569. X    *s++ = (*prot & FIBF_PURE)      ? 'p' : '-';
  570. X    *s++ = (*prot & FIBF_ARCHIVE)   ? 'a' : '-';
  571. X    *s++ = (*prot & FIBF_READ)      ? '-' : 'r';
  572. X    *s++ = (*prot & FIBF_WRITE)     ? '-' : 'w';
  573. X    *s++ = (*prot & FIBF_EXECUTE)   ? '-' : exbit;
  574. X    *s++ = (*prot & FIBF_DELETE)    ? '-' : 'd';
  575. X    *s     = '\0';
  576. X
  577. X    if (*prot & FIBF_RESERVED) {
  578. X    if (*prot & FIBF_HIDDEN) {
  579. X        t[1] = 'H';
  580. X    } else {
  581. X        t[1] = '+';
  582. X    }
  583. X    return(1);
  584. X    }
  585. X    return(0);
  586. X}
  587. X
  588. X
  589. X/*
  590. X *  fixNumBlocks() - A hack to fix the fib_NumBlocks field so it is correct.
  591. X *
  592. X *  It was busted with the introduction of the FFS in AmigaDOS 1.3.  Don't
  593. X *  ask where this is documented ... as far as I know, it isn't not in the
  594. X *  1.3 includes, autodocs, readme's, user's manual, RKM's, or DevCon notes.
  595. X *  It is possible it may be mentioned in an AmigaMail (that wonderfully
  596. X *  regular publication), and I'm told it was mentioned once in a message on
  597. X *  USENET.
  598. X *
  599. X *  Helluva way to run a ship ...
  600. X *
  601. X *  As may be ... since fib_NumBlocks was in use thruout this code before
  602. X *  this was discovered, it was easiest to just fixup the fib_NumBlocks when
  603. X *  the file/dir gets initially Examine/ExNext'd,  with a call to Info() for
  604. X *  *all* files (which means alot of unnecessary calls get made [for files
  605. X *  all in the same dir, etc]).  The performance penalty ends up being about
  606. X *  1 sec, for a tree with 537 entries (which seems acceptable to me).
  607. X *
  608. X *  If you don't like it ... I suggest you complain to CBM about their, er,
  609. X *  uhmm, "documentation" efforts!
  610. X *
  611. X *  Helluva way to run a ship ...
  612. X *
  613. X *  /kim   /\;;/\
  614. X *
  615. X */
  616. X
  617. XVOID fixNumBlocks(lock, fib)
  618. X  struct FileLock *lock;
  619. X  FIB *fib;
  620. X{
  621. X    LONG *nb;
  622. X    LONG bsize;
  623. X    static int errflag = 0;
  624. X
  625. X    if ((LSFlagsX & NOFIXNUMBLOCKS) != 0) return;
  626. X
  627. X    if (blksize == 0)
  628. X    {
  629. X      /* Try to fill InfoData, bomb if not readable for some reason (like */
  630. X      if (Info((BPTR)lock, CurID) == 0)   /* ls'ing a "pathass'd" assign) */
  631. X      {
  632. X    /* Print error msg only once, and only if we'll be printing block counts */
  633. X    if ((errflag == 0) && ((LSFlags & (LONGLIST | TOTALIZE)) != 0))
  634. X    {
  635. X      errflag++;
  636. X      asprintf(workstr, "\nls: warning (%ld): block count(s) may be inaccurate - see ls.doc\n\n", IoErr());
  637. X      WSTR(workstr);
  638. X    }
  639. X    return;
  640. X      }
  641. X      else
  642. X    bsize = CurID->id_BytesPerBlock;
  643. X    }
  644. X    else
  645. X     bsize = blksize;
  646. X
  647. X     nb = (LONG *)&(fib->fib_NumBlocks);
  648. X    *nb = (fib->fib_Size / bsize) + ((fib->fib_Size % bsize) ? 1 : 0);
  649. X}
  650. X
  651. X
  652. X/*
  653. X *  blkalloc() - Returns the actual number of blocks allocated by a file/dir
  654. X *         (or just the data blocks, if DATABLKSONLY is set).
  655. X *
  656. X *  Assumes 1.3 original or fast filesystem (and a fixed up fib_NumBlocks).
  657. X *
  658. X */
  659. X
  660. XLONG blkalloc(fib)
  661. X  register FIB *fib;
  662. X{
  663. X    if ((LSFlagsX & DATABLKSONLY) == 0) {
  664. X      return(fib->fib_NumBlocks +
  665. X        (fib->fib_NumBlocks / MAX_BLKS_PER_EXTENT) +
  666. X       ((fib->fib_NumBlocks % MAX_BLKS_PER_EXTENT) ? 1 : 0) +
  667. X       ((fib->fib_Size == 0) ? 1 : 0)  /* kludge for 0-len files (and dirs) */
  668. X        );
  669. X    } else {
  670. X      return(fib->fib_NumBlocks);
  671. X    }
  672. X}
  673. X
  674. X
  675. X/*
  676. X *  llistentry() - Prints the data in a FIB entry.
  677. X *
  678. X */
  679. X
  680. XVOID llistentry(fib)
  681. X  register FIB *fib;
  682. X{
  683. X    BYTE tbuf[64]; /* shouldn't exceed about 47-53 chars max (but add some pad) */
  684. X    LONG len;
  685. X
  686. X    attrstr(workstr, &(fib->fib_DirEntryType), &(fib->fib_Protection));
  687. X
  688. X    if (fib->fib_Comment[0] == 0) {
  689. X    strcat(workstr, "  ");
  690. X    } else {
  691. X    strcat(workstr, " c");
  692. X    }
  693. X
  694. X    if ((LSFlags & SHOWDISKKEYS) != 0)
  695. X    {
  696. X      asprintf(tbuf, " %6ld", fib->fib_DiskKey);
  697. X      strcat(workstr, tbuf);
  698. X    }
  699. X
  700. X    asprintf(tbuf, " %5ld %8ld  ", blkalloc(fib), fib->fib_Size);
  701. X    strcat(workstr, tbuf);
  702. X
  703. X    datestr(tbuf, &(fib->fib_Date), datefmt);
  704. X    strcat(workstr, tbuf);
  705. X    strcat(workstr, "  ");
  706. X
  707. X    WSTR(workstr);
  708. X
  709. X    if (fib->fib_DirEntryType > 0) SetConPen(penstr3);
  710. X
  711. X    *workstr = '\0';
  712. X    if (((LSFlags & RELPATHNAMES) != 0) || ((LSFlags & FULLPATHNAMES) != 0))
  713. X    {
  714. X      strcat(workstr, curpath);
  715. X      len = strlen(workstr);
  716. X      if ((len > 1) && (*(workstr + len - 1) != ':'))
  717. X    strcat(workstr, "/");
  718. X    }
  719. X
  720. X    strcat(workstr, fib->fib_FileName);
  721. X
  722. X    len = 0;
  723. X    if ((LSFlags & RELPATHNAMES) != 0)
  724. X    {
  725. X      len = strlen(initialpath);
  726. X      if ((strnicmp(workstr, initialpath, len) == 0) && (len < strlen(workstr)))
  727. X      {
  728. X    if (*(workstr + len) == '/')
  729. X      len++;
  730. X      }
  731. X      else
  732. X    len = 0;
  733. X    }
  734. X
  735. X    if (((LSFlags & ADDDIRSLASH) != 0) && (fib->fib_DirEntryType > 0))
  736. X      strcat(workstr, "/");
  737. X
  738. X/* commented out until there is more support for NOT defaulting to "x" */
  739. X/*  if (workstr[0] == 'd') {
  740. X *    strcat(workstr, "/");
  741. X *  } else if (workstr[7] != '-') {
  742. X *    strcat(workstr, "*");
  743. X *  }
  744. X */
  745. X
  746. X    strcat(workstr, "\n");
  747. X    WSTR(workstr + len);
  748. X
  749. X    if (fib->fib_DirEntryType > 0) SetConPen(penstr0);
  750. X
  751. X    if (((LSFlags & NOTEFLAG) != 0) && (fib->fib_Comment[0] != 0))
  752. X    {
  753. X      SetConPen(penstr3);
  754. X      asprintf(workstr, "\"%s\"\n", fib->fib_Comment);
  755. X      WSTR (workstr);
  756. X      SetConPen(penstr0);
  757. X    }
  758. X}
  759. X
  760. X
  761. X/* --------------------------------------------------------------------
  762. X * Allocate a FibEntry structure and associated FileInfoBlock.
  763. X * -------------------------------------------------------------------- */
  764. Xstruct FibEntry *AllocFib()
  765. X{
  766. X  struct FibEntry *tfibp;
  767. X
  768. X  if ((tfibp = myalloc((LONG)(sizeof(struct FibEntry) + sizeof(struct FileInfoBlock)))) != 0)
  769. X  {
  770. X    tfibp->fe_Fib = (struct FileInfoBlock *)((ULONG)tfibp + sizeof(struct FibEntry));
  771. X  }
  772. X  else
  773. X    LSFlags |= BREAKFLAG;
  774. X  return(tfibp);
  775. X}
  776. X
  777. X
  778. X/* --------------------------------------------------------------------
  779. X * Use AmigaDOS to output a string to the stdout channel
  780. X * -------------------------------------------------------------------- */
  781. XVOID WSTR(tstring)
  782. X  BYTE *tstring;
  783. X{
  784. X  LONG i;
  785. X
  786. X  i = strlen(tstring);
  787. X  if (i > 0)
  788. X  {
  789. X    (VOID) Write((BPTR)ConOut, tstring, i);
  790. X  }
  791. X}
  792. X
  793. X
  794. X/* --------------------------------------------------------------------
  795. X * Use AmigaDOS to put a character to the stdout
  796. X * -------------------------------------------------------------------- */
  797. XVOID WCHR(ch)
  798. X  BYTE *ch;
  799. X{
  800. X  (VOID) Write((BPTR)ConOut, ch, 1L);
  801. X}
  802. X
  803. X
  804. X/* --------------------------------------------------------------------
  805. X * Check to see if the user hit ^C
  806. X *
  807. X * Coded in this arcane fashion since there seems to be a race/etc. between
  808. X * the SetSignal() call, and the WSTR() and or'ing in of the BREAKFLAG.  If
  809. X * the outer "if" is omitted, "**BREAK" prints 3 times; if the outer "else"
  810. X * is omitted, the BREAKFLAG doesn't seem to get or'd in ... grumble, swear!
  811. X *
  812. X * As my be ... this is an ugly hack, but it seems to work ...
  813. X *
  814. X * C'est la vie ... /kim
  815. X *
  816. X * -------------------------------------------------------------------- */
  817. XVOID TestBreak ()
  818. X{
  819. X  if ((oldsig & SIGBREAKF_CTRL_C) == 0)
  820. X  {
  821. X    oldsig = SetSignal (0L, 0L);
  822. X    if ((oldsig & SIGBREAKF_CTRL_C) != 0)
  823. X    {
  824. X      WSTR ("\2330m\233 p**BREAK\n");
  825. X      LSFlags |= BREAKFLAG;
  826. X    }
  827. X  }
  828. X  else
  829. X  {
  830. X    LSFlags |= BREAKFLAG;
  831. X  }
  832. X}
  833. X
  834. X
  835. X/* --------------------------------------------------------------------
  836. X * Prompt the user to hit return, wait till return is hit
  837. X * -------------------------------------------------------------------- */
  838. XVOID PagePrompt (page, maxpage)
  839. X  LONG page, maxpage;
  840. X{
  841. X  if ((CurWinCols > maxnamlen) && (page > 1) && ((LSFlags & NOINTERACT) == 0))
  842. X  {
  843. X    SetConPen(penstr6);
  844. X    asprintf(workstr, " More (%ld of %ld) ... ", (page - 1), maxpage);
  845. X    if (CurWinCols <= strlen(workstr))
  846. X      strcpy(workstr, "More ... ");
  847. X    WSTR(workstr);
  848. X    SetConPen(penstr0);
  849. X    (VOID) Read((BPTR)ConIn, workstr, (long)WORKSIZE);
  850. X    WSTR("\2330 p\233F\233K");
  851. X    TestBreak();
  852. X  }
  853. X}
  854. X
  855. X
  856. X/* -------------------------------------------------------------------- */
  857. Xstruct FibEntry *ModNextFib(tfibp, rows)
  858. X  struct FibEntry *tfibp;
  859. X  LONG rows;
  860. X{
  861. X  LONG i;
  862. X
  863. X  for (i = 0; i < rows && tfibp->fe_Node.mln_Succ != 0; i++)
  864. X  {
  865. X    tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ;
  866. X  }
  867. X  return(tfibp);
  868. X}
  869. X
  870. X
  871. X/* --------------------------------------------------------------------
  872. X * set CON: character color to default Pen1 colors
  873. X * -------------------------------------------------------------------- */
  874. XVOID SetConPen (penstr)
  875. X  BYTE *penstr;
  876. X{
  877. X  if ((LSFlags & CONSOLE) != 0)
  878. X    WSTR (penstr);
  879. X}
  880. X
  881. X
  882. X/* #define DEBUGSLD 1 */
  883. X/* --------------------------------------------------------------------
  884. X * List a FibEntry list in a compact fashion
  885. X * -------------------------------------------------------------------- */
  886. XVOID SListDir (fibheadp)
  887. X  struct List *fibheadp;
  888. X{
  889. X  LONG avglen;
  890. X  LONG colcnt;
  891. X  LONG currow;
  892. X  LONG dfcount;
  893. X  LONG i, j, wlen;
  894. X  LONG maxcol;
  895. X  LONG maxpage;
  896. X  LONG maxrow;
  897. X  LONG maxwinrow;
  898. X  LONG minpad = 2;  /* minimum number of blanks between listing entries */
  899. X  LONG pagecnt;
  900. X  LONG rowcnt;
  901. X  LONG tlen;
  902. X  LONG totlen;
  903. X  struct FibEntry *hfibp, *tfibp;
  904. X
  905. X  SetConPen (penstr1);      /* Turn the cursor off since it will blink anyway */
  906. X  GetWinBounds (&colcnt, &currow);  /* Get current window size */
  907. X
  908. X  if (CurWinCols == 0)
  909. X    CurWinCols = colcnt;
  910. X  if (CurWinRows == 0)
  911. X    CurWinRows = currow;
  912. X
  913. X  if ((LSFlags & VARCOLSFORMAT) != 0)
  914. X  {
  915. X  /* Make a average-case WxH estimate for # of display columns */
  916. X    for (totlen = dfcount = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  917. X     hfibp->fe_Node.mln_Succ != 0;
  918. X     hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  919. X    {
  920. X      if (hfibp->fe_Fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) == 0)
  921. X    (VOID) strcat (hfibp->fe_Fib->fib_FileName, SlashStr);
  922. X      totlen += strlen (hfibp->fe_Fib->fib_FileName);
  923. X      dfcount++;
  924. X      if (((LSFlags & WILDPATH) != 0) && (hfibp->fe_Fib->fib_DirEntryType > 0))
  925. X      {
  926. X    totlen -= strlen (hfibp->fe_Fib->fib_FileName);
  927. X    dfcount--;
  928. X      }
  929. X    }
  930. X    if (dfcount == 0) dfcount++;
  931. X
  932. X  /* Calc average length of all entries */
  933. X    avglen = totlen / dfcount;          /* Avg name length = totlen/numentries */
  934. X    if ((totlen % dfcount) != 0)
  935. X      avglen++;
  936. X
  937. X  /* Longest name wider than window, or single column flag set? */
  938. X    if ((CurWinCols <= maxnamlen) || ((LSFlags & ONECOLFORMAT) != 0))
  939. X      maxcol = 1;   /* Yep, just print one column */
  940. X    else
  941. X    {
  942. X    /* Else maxcols = winwidth/namewidth */
  943. X      for (maxcol = 0, colcnt = CurWinCols; colcnt >= avglen; maxcol++)
  944. X      {
  945. X    colcnt -= avglen + 2;
  946. X      }
  947. X    }
  948. X#ifdef DEBUGSLD
  949. X    asprintf(workstr, "avg:%ld max:%ld\n", avglen, maxnamlen); WSTR(workstr);
  950. X#endif
  951. X
  952. X  /* Dry run output avg-case WxH table to see if it needs adjusting */
  953. X    for (;;)
  954. X    {
  955. X    /* Clear out previous padtab */
  956. X      memset(padtab, 0, PADTABSIZE);
  957. X
  958. X    /* Number of rows = total entries / entries per row */
  959. X      maxrow = dfcount / maxcol;
  960. X      if ((dfcount % maxcol) != 0)        /* Round up if non-integral */
  961. X    maxrow++;
  962. X#ifdef DEBUGSLD
  963. X      asprintf(workstr, "avg: %ld rows by %ld cols\n", maxrow, maxcol); WSTR(workstr);
  964. X#endif
  965. X
  966. X      for (rowcnt = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  967. X       rowcnt < maxrow && hfibp->fe_Node.mln_Succ != 0;
  968. X       rowcnt++, hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  969. X      {
  970. X    for (colcnt = 0, tfibp = hfibp;
  971. X         colcnt < maxcol && tfibp->fe_Node.mln_Succ != 0;
  972. X         colcnt++, tfibp = ModNextFib (tfibp, maxrow))
  973. X    {
  974. X      if (~(((LSFlags & WILDPATH) != 0) && (tfibp->fe_Fib->fib_DirEntryType > 0)))
  975. X      {
  976. X        tlen = strlen (tfibp->fe_Fib->fib_FileName);
  977. X        if (tlen > padtab[colcnt])
  978. X          padtab[colcnt] = tlen;
  979. X      }
  980. X    }
  981. X
  982. X      /* If this is the first row, calc actual maxcol/maxrow for this dfcount */
  983. X    if (rowcnt == 0)
  984. X    {
  985. X      maxcol = colcnt;
  986. X      maxrow = dfcount / maxcol;
  987. X      if ((dfcount % maxcol) != 0)    /* Round up if non-integral */
  988. X        maxrow++;
  989. X    }
  990. X      }
  991. X
  992. X    /* Calculate actual total width by adding up width of all columns */
  993. X      for (colcnt = totlen = 0; (colcnt + 1) < maxcol; colcnt++)
  994. X      {
  995. X    totlen += (LONG)padtab[colcnt] + 2;
  996. X#ifdef DEBUGSLD
  997. X    asprintf(workstr, "padtab[%ld]=%ld\n", colcnt, (LONG)padtab[colcnt]); WSTR(workstr);
  998. X#endif
  999. X      }
  1000. X      totlen += (LONG)padtab[colcnt];
  1001. X#ifdef DEBUGSLD
  1002. X      asprintf(workstr, "padtab[%ld]=%ld\n", colcnt, (LONG)padtab[colcnt]); WSTR(workstr);
  1003. X      asprintf(workstr, "totlen %ld\n", totlen); WSTR(workstr);
  1004. X#endif
  1005. X
  1006. X    /* if More than one column and
  1007. X     * total width of all columns is greater > our window width,
  1008. X     * then decrease number of display columns
  1009. X     */
  1010. X      if ((maxcol > 1) && (totlen > CurWinCols))
  1011. X      {
  1012. X    maxcol--;
  1013. X#ifdef DEBUGSLD
  1014. X    asprintf(workstr, "new maxcol:%ld\n", maxcol); WSTR(workstr);
  1015. X#endif
  1016. X      }
  1017. X      else
  1018. X    break;
  1019. X    }
  1020. X#ifdef DEBUGSLD
  1021. X    asprintf(workstr, "adjusted: maxrow:%ld maxcol:%ld (winwidth:%ld totlen:%ld)\n", maxrow, maxcol, CurWinCols, totlen); WSTR(workstr);
  1022. X#endif
  1023. X
  1024. X  }
  1025. X  else
  1026. X  {
  1027. X
  1028. X    if ((LSFlags & ADDDIRSLASH) != 0)
  1029. X      maxnamlen += 1;
  1030. X
  1031. X    for (dfcount = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  1032. X     hfibp->fe_Node.mln_Succ != 0;
  1033. X     hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  1034. X    {
  1035. X      if ((LSFlags & ADDDIRSLASH) != 0)
  1036. X    if (hfibp->fe_Fib->fib_DirEntryType > 0)
  1037. X      (VOID) strcat (hfibp->fe_Fib->fib_FileName, SlashStr);
  1038. X    else
  1039. X      (VOID) strcat (hfibp->fe_Fib->fib_FileName, " ");
  1040. X      dfcount++;
  1041. X      if (((LSFlags & WILDPATH) != 0) && (hfibp->fe_Fib->fib_DirEntryType > 0))
  1042. X    dfcount--;
  1043. X    }
  1044. X
  1045. X    if ((CurWinCols <= maxnamlen) || ((LSFlags & ONECOLFORMAT) != 0))
  1046. X      maxcol = 1;
  1047. X    else
  1048. X      maxcol = CurWinCols / (maxnamlen + minpad);
  1049. X    maxrow = dfcount / maxcol;
  1050. X    if ((dfcount % maxcol) != 0)
  1051. X      maxrow++;
  1052. X    memset(padtab, 0, PADTABSIZE);
  1053. X    for (colcnt = 0; colcnt < maxcol; colcnt++)
  1054. X      padtab[colcnt] = maxnamlen;
  1055. X  }
  1056. X
  1057. X/* Calc number of pages */
  1058. X  maxwinrow = CurWinRows - 1;    /* was -3 in v3.1 */
  1059. X  if (maxwinrow <= 0)
  1060. X    maxwinrow = 1;
  1061. X  pagecnt = 1;
  1062. X  maxpage = maxrow / maxwinrow;
  1063. X  if ((maxrow % maxwinrow) != 0)
  1064. X    maxpage++;
  1065. X
  1066. X/* Do actual output scan */
  1067. X  for (rowcnt = 0, currow = maxwinrow, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  1068. X       (LSFlags & BREAKFLAG) == 0 && rowcnt < maxrow && hfibp->fe_Node.mln_Succ != 0;
  1069. X       TestBreak(), rowcnt++, currow++, hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  1070. X  {
  1071. X    if ((maxpage > 1) && (currow == maxwinrow))
  1072. X    {
  1073. X      currow = 0;
  1074. X      if ((LSFlags & CONSOLE) != 0)
  1075. X      {
  1076. X    PagePrompt (pagecnt, maxpage);
  1077. X    if ((LSFlags & BREAKFLAG) != 0)
  1078. X      break;
  1079. X      }
  1080. X      pagecnt++;
  1081. X    }
  1082. X
  1083. X    for (colcnt = 0, tfibp = hfibp;
  1084. X     colcnt < maxcol && tfibp->fe_Node.mln_Succ != 0;
  1085. X     colcnt++)
  1086. X    {
  1087. X      if (tfibp->fe_Fib->fib_DirEntryType < 0)
  1088. X      {
  1089. X      /* Print a file entry */
  1090. X    (VOID) stpcpy (workstr, tfibp->fe_Fib->fib_FileName);
  1091. X    wlen = strlen (workstr);
  1092. X      }
  1093. X      else
  1094. X      {
  1095. X      /* Print a directory entry */
  1096. X    workstr[0] = 0;
  1097. X    wlen = 0;
  1098. X
  1099. X    if ((LSFlags & WILDPATH) == 0)
  1100. X    {
  1101. X      wlen = strlen (tfibp->fe_Fib->fib_FileName);
  1102. X      if ((LSFlags & CONSOLE) != 0)
  1103. X        (VOID) strcat (workstr, penstr3);
  1104. X      (VOID) strcat (workstr, tfibp->fe_Fib->fib_FileName);
  1105. X      if ((LSFlags & CONSOLE) != 0)
  1106. X        (VOID) strcat (workstr, penstr0);
  1107. X    }
  1108. X      }
  1109. X
  1110. X    /* Move along list to the next entry, mod maxcol, print this entry */
  1111. X      tfibp = ModNextFib (tfibp, maxrow);
  1112. X
  1113. X    /* If this is not the last column, pad with spaces till we get to next column */
  1114. X      if ((colcnt + 1) < maxcol && tfibp->fe_Node.mln_Succ != 0)
  1115. X      {
  1116. X    for (i = (LONG)padtab[colcnt] - 1 + minpad, j = strlen (workstr); i >= wlen; i--, j++)
  1117. X      workstr[j] = ' ';
  1118. X    workstr[j] = 0;
  1119. X      }
  1120. X    /* Output the final entry */
  1121. X      WSTR(workstr);
  1122. X    }
  1123. X
  1124. X  /* Filled this row, start next down */
  1125. X    WCHR(NLine);
  1126. X  }
  1127. X  SetConPen(penstr2);         /* Turn cursor back on */
  1128. X}
  1129. X
  1130. X
  1131. X/* -------------------------------------------------------------------- */
  1132. XBYTE *GetDecNum (cp, spccnt)
  1133. X  BYTE *cp;
  1134. X  LONG *spccnt;
  1135. X{
  1136. X  for (*spccnt = 0; *cp >= '0' && *cp <= '9'; cp++)
  1137. X  {
  1138. X    *spccnt = *spccnt * 10 + (LONG)*cp - '0';
  1139. X  }
  1140. X  return (cp);
  1141. X}
  1142. X
  1143. X
  1144. X/* -------------------------------------------------------------------- */
  1145. XVOID ParseFormatOpts (fib)
  1146. X  struct FileInfoBlock *fib;
  1147. X{
  1148. X  BYTE *cp1, *cp2, *reps;
  1149. X  BYTE *pathend, *thenamestr;
  1150. X  LONG i, spccnt;
  1151. X
  1152. X  i = strlen (curpath);
  1153. X  pathend = curpath + i;
  1154. X  if (i > 1 && *(curpath + i - 1) != ':')
  1155. X  {
  1156. X    *(curpath + i) = '/';
  1157. X    i++;
  1158. X    *(curpath + i) = 0;
  1159. X  }
  1160. X  thenamestr = curpath + strlen(curpath);
  1161. X  cp2 = thenamestr;
  1162. X  if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0)
  1163. X    cp2 = stpcpy (cp2, penstr3);
  1164. X  cp2 = stpcpy (cp2, fib->fib_FileName);
  1165. X  if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0)
  1166. X    (VOID) stpcpy (cp2, penstr0);
  1167. X
  1168. X  for (cp1 = workstr, cp2 = thefmtstr; *cp2 != 0; cp2++)
  1169. X  {
  1170. X    if (*cp2 != '%' && *cp2 != '\\')
  1171. X      *cp1++ = *cp2;
  1172. X    else
  1173. X    {
  1174. X      if (*cp2 == '%')
  1175. X      {
  1176. X    cp2++;
  1177. X    cp2 = GetDecNum (cp2, &spccnt);
  1178. X    if (spccnt > 99)
  1179. X    {
  1180. X      spccnt = 99;
  1181. X    }
  1182. X
  1183. X    switch (*cp2)
  1184. X    {
  1185. X      case 'p':
  1186. X        reps = theprotstr;
  1187. X        break;
  1188. X      case 'd':
  1189. X        reps = thedatestr;
  1190. X        break;
  1191. X      case 't':
  1192. X        reps = thetimestr;
  1193. X        break;
  1194. X      case 'b':
  1195. X        reps = theblksstr;
  1196. X        break;
  1197. X      case 's':
  1198. X        reps = thesizestr;
  1199. X        break;
  1200. X      case 'n':
  1201. X        if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0 && spccnt > 0)
  1202. X          spccnt += 7;
  1203. X        if ((LSFlags & FULLPATHNAMES) != 0)
  1204. X          reps = curpath;
  1205. X        else
  1206. X          reps = thenamestr;
  1207. X        break;
  1208. X      case '%':
  1209. X        *cp1++ = '%';
  1210. X        *cp1++ = 0;
  1211. X      default:
  1212. X        reps = NULLSTR;
  1213. X    }
  1214. X    for (i = strlen(reps); i < spccnt; i++)
  1215. X      *cp1++ = ' ';
  1216. X    cp1 = stpcpy (cp1, reps);
  1217. X      }
  1218. X      else
  1219. X      {
  1220. X    cp2++;
  1221. X    switch (*cp2)
  1222. X    {
  1223. X      case 'n':
  1224. X        *cp1++ = '\n';
  1225. X        break;
  1226. X      case 't':
  1227. X        *cp1++ = '\t';
  1228. X        break;
  1229. X      case '\\':
  1230. X        *cp1++ = '\\';
  1231. X        break;
  1232. X    }
  1233. X    *cp1 = 0;
  1234. X      }
  1235. X    }
  1236. X  }
  1237. X  WSTR(workstr);
  1238. X
  1239. X  if ((LSFlags & NOTEFLAG) != 0 && fib->fib_Comment[0] != 0)
  1240. X  {
  1241. X    SetConPen(penstr3);
  1242. X    (VOID)asprintf(workstr, "/* %s */\n", fib->fib_Comment);
  1243. X    WSTR (workstr);
  1244. X    SetConPen(penstr0);
  1245. X  }
  1246. X  *pathend = 0;
  1247. X}
  1248. X
  1249. X
  1250. X/* --------------------------------------------------------------------
  1251. X * Verbosely list a particular FibEntry
  1252. X * -------------------------------------------------------------------- */
  1253. XVOID LListEntry (fib)
  1254. X  struct FileInfoBlock *fib;
  1255. X{
  1256. X  LONG i;
  1257. X  LONG    pmodes;
  1258. X
  1259. X  pmodes = fib->fib_Protection & 0xff;
  1260. X  (VOID)stpcpy (theprotstr, "chsparw7d");
  1261. X  theprotstr[7] = exbit;
  1262. X  for (i = 3; i >=  0; i--)
  1263. X  {
  1264. X    if ((pmodes & (1 << i)) != 0)
  1265. X      theprotstr[8 - i] = '-';
  1266. X    if ((pmodes & (1 << (i + 4))) == 0)
  1267. X      theprotstr[4 - i] = '-';
  1268. X  }
  1269. X
  1270. X  if (fib->fib_Comment[0] == 0)
  1271. X    theprotstr[0] = '-';
  1272. X
  1273. X  FibFileDate(&fib->fib_Date, thedatestr, thetimestr);
  1274. X
  1275. X  if (fib->fib_DirEntryType > 0)
  1276. X  {
  1277. X    if ((LSFlagsX & DATABLKSONLY) != 0)
  1278. X      stpcpy(theblksstr, "0");
  1279. X    else
  1280. X      stpcpy(theblksstr, "1");
  1281. X    stpcpy (thesizestr, "Dir");
  1282. X  }
  1283. X  else
  1284. X  {
  1285. X    asprintf(theblksstr, LongFmtStr, blkalloc(fib));
  1286. X    asprintf(thesizestr, LongFmtStr, fib->fib_Size);
  1287. X  }
  1288. X  ParseFormatOpts(fib);
  1289. X}
  1290. X
  1291. X
  1292. X/* --------------------------------------------------------------------
  1293. X * List a directory in a verbose informative manner
  1294. X * -------------------------------------------------------------------- */
  1295. XVOID LListDir (fibheadp)
  1296. X  struct List *fibheadp;
  1297. X{
  1298. X  struct FibEntry *tfibp;
  1299. X
  1300. X  SetConPen(penstr1);
  1301. X
  1302. X  totblocks = totbytes = 0;
  1303. X  for (tfibp = (struct FibEntry *)fibheadp->lh_Head;
  1304. X       tfibp->fe_Node.mln_Succ != 0;
  1305. X       tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ)
  1306. X  {
  1307. X    TestBreak();
  1308. X    if ((LSFlags & BREAKFLAG) != 0)
  1309. X      return;
  1310. X
  1311. X    if ((tfibp->fe_Fib->fib_DirEntryType > 0) && ((LSFlags & WILDPATH) != 0))
  1312. X    {
  1313. X      ;    /* seems to be a Lattice v5.04a bug ... negating the test and putting the "else" here doesn't work */
  1314. X    }
  1315. X    else
  1316. X    {
  1317. X      if ((LSFlags & OLDLONGFORMAT) != 0)
  1318. X    LListEntry (tfibp->fe_Fib);
  1319. X      else
  1320. X    llistentry (tfibp->fe_Fib);
  1321. X
  1322. X      totblocks += blkalloc(tfibp->fe_Fib);
  1323. X      totbytes    += tfibp->fe_Fib->fib_Size;
  1324. X    }
  1325. X  }
  1326. X
  1327. X  if ((LSFlags & (BREAKFLAG | NODIRTOTAL)) == 0)
  1328. X  {
  1329. X    if ((LSFlags & WILDPATH) == 0)
  1330. X    {
  1331. X      if ((dircount + filecount) > 1)
  1332. X      {
  1333. X    asprintf(workstr, totalfmtstr, dircount, filecount, totblocks, totbytes);
  1334. X    WSTR(workstr);
  1335. X      }
  1336. X    }
  1337. X    else
  1338. X    {
  1339. X      if (filecount > 1)
  1340. X      {
  1341. X    asprintf(workstr, filefmtstr, filecount, totblocks, totbytes);
  1342. X    WSTR(workstr);
  1343. X      }
  1344. X    }
  1345. X  }
  1346. X  SetConPen(penstr2);
  1347. X}
  1348. X
  1349. X
  1350. X/* -------------------------------------------------------------------- */
  1351. XLONG CmpDateBounds (tdate)
  1352. X  struct DateStamp *tdate;
  1353. X{
  1354. X  if ((LSFlags & SHOWNEWERTHAN) != 0)
  1355. X  {
  1356. X    if (CompareDateStamps(&thenewdate, tdate) >= 0)
  1357. X      return (0L);
  1358. X  }
  1359. X  if ((LSFlags & SHOWOLDERTHAN) != 0)
  1360. X  {
  1361. X    if (CompareDateStamps(tdate, &theolddate) >= 0)
  1362. X      return (0L);
  1363. X  }
  1364. X  return (1L);
  1365. X}
  1366. X
  1367. X
  1368. X/* --------------------------------------------------------------------
  1369. X * Free up memory allocated to a linked list of FibEntrys
  1370. X * -------------------------------------------------------------------- */
  1371. XVOID FreeAllFibs (fibheadp)
  1372. X  struct List *fibheadp;
  1373. X{
  1374. X  struct FibEntry *tfibp;
  1375. X
  1376. X  if (fibheadp != 0)
  1377. X  {
  1378. X    while (fibheadp->lh_Head->ln_Succ != 0)
  1379. X    {
  1380. X      tfibp = (struct FibEntry *)RemTail(fibheadp);
  1381. X      myfree(tfibp);
  1382. X    }
  1383. X
  1384. X  /* Now free the MinList itself */
  1385. X    myfree(fibheadp);
  1386. X  }
  1387. X}
  1388. X
  1389. X
  1390. X/* --------------------------------------------------------------------
  1391. X * Allocate and fill a linked list of FileInfoBlocks
  1392. X * -------------------------------------------------------------------- */
  1393. Xstruct List *GetDir (lockp, fibp)
  1394. X  struct FileLock *lockp;
  1395. X  struct FileInfoBlock *fibp;
  1396. X{
  1397. X  BYTE *thepat;            /* Pattern pointer to dir or file pattern */
  1398. X  LONG dfcount;
  1399. X  LONG matchstat;           /* Result of wildmatch() */
  1400. X  LONG nextstat;           /* Status of ExNext() */
  1401. X  LONG tempnamlen;           /* Compare length for strings */
  1402. X  LONG tmpflags;
  1403. X  struct List *fibhead;        /* Temp list of Fibs created */
  1404. X  struct List *dirhead;        /* Temp list of dirs if SHOWDIRS == 0 */
  1405. X
  1406. X  maxnamlen = dfcount = dircount = filecount = 0L;
  1407. X
  1408. X/* Initialize an exec list of nodes, zero entries */
  1409. X  if ((fibhead = myalloc ((LONG)sizeof(struct MinList))) == 0)
  1410. X    return (0L);
  1411. X  NewList (fibhead);
  1412. X
  1413. X/* Allocate an separate list for directories that don't match specs */
  1414. X  if ((dirhead = myalloc((LONG)sizeof(struct MinList))) == 0)
  1415. X    goto BADALLOC;
  1416. X  NewList (dirhead);
  1417. X
  1418. X  do
  1419. X  {
  1420. X    TestBreak ();
  1421. X    if ((LSFlags & BREAKFLAG) != 0)
  1422. X      goto GOODRET;
  1423. X
  1424. X  /* If we got something */
  1425. X    if ((nextstat = ExNext ((BPTR)lockp, fibp)) != 0)
  1426. X    {
  1427. X    /* If the entry is wanted bump count of files or directories */
  1428. X      if (CmpDateBounds (&fibp->fib_Date) != 0)
  1429. X      {
  1430. X    if (fibp->fib_DirEntryType > 0) /* It's a directory */
  1431. X      thepat = theDirPat;
  1432. X    else
  1433. X      thepat = theFilePat;
  1434. X
  1435. X    matchstat = wildmatch (fibp->fib_FileName, thepat);
  1436. X
  1437. X    if ((LSFlags & MATCHINFOFILES) == 0 && matchstat != 0)
  1438. X    {
  1439. X      tmpflags  = LSFlags;
  1440. X      LSFlags  |= IGNORECASEWILD;
  1441. X      matchstat = wildmatch (fibp->fib_FileName, "*.info") ^ 1;
  1442. X      LSFlags   = tmpflags;
  1443. X    }
  1444. X    if ((LSFlags & MATCHDOTFILES) == 0 && matchstat != 0)
  1445. X      matchstat = wildmatch (fibp->fib_FileName, ".*") ^ 1;
  1446. X    if ((LSFlags & ANTIMATCH) != 0 && matchstat != 0)
  1447. X      matchstat = wildmatch (fibp->fib_FileName, theAntiPat) ^ 1;
  1448. X    if ((fibp->fib_Protection & FIBF_HIDDEN) != 0)
  1449. X      matchstat = (LSFlags & SHOWHIDDEN) ? 1 : 0;
  1450. X
  1451. X    if (matchstat == 0)     /* No match? Then move on */
  1452. X      continue;
  1453. X
  1454. X    dfcount++;  /* we found something of interest */
  1455. X
  1456. X    if (fibp->fib_DirEntryType > 0) /* It's a directory */
  1457. X    {
  1458. X      if (FillFibEntry (dirhead, fibp) == 0)
  1459. X        goto BADALLOC;
  1460. X      if ((LSFlags & SHOWDIRS) != 0)
  1461. X      {
  1462. X        dircount++; gdircount++; gitemcnt++;
  1463. X
  1464. X        if ((LSFlagsX & DATABLKSONLY) == 0)
  1465. X          gtotblocks++;
  1466. X      }
  1467. X      else
  1468. X        continue;
  1469. X    }
  1470. X    else                /* It's a file entry */
  1471. X    {
  1472. X      if ((LSFlags & SHOWFILES) != 0)
  1473. X      {
  1474. X        filecount++; gfilecount++; gitemcnt++;
  1475. X        fixNumBlocks(lockp, fibp);
  1476. X        gtotblocks += blkalloc(fibp);
  1477. X        gtotbytes  += fibp->fib_Size;
  1478. X      }
  1479. X      else              /* Don't want this file, move on to next entry */
  1480. X        continue;
  1481. X    }
  1482. X
  1483. X      /* See if this is the longest filename for later use in listing */
  1484. X    tempnamlen = strlen (fibp->fib_FileName);
  1485. X    if (tempnamlen > maxnamlen)
  1486. X    {
  1487. X      if (fibp->fib_DirEntryType < 0)
  1488. X      {
  1489. X        maxnamlen = tempnamlen;
  1490. X      }
  1491. X      else if ((LSFlags & WILDPATH) == 0)
  1492. X        maxnamlen = tempnamlen;
  1493. X    }
  1494. X
  1495. X      /* Allocate another FibEntry, put the info inside */
  1496. X    if (FillFibEntry (fibhead, fibp) == 0)
  1497. X      goto BADALLOC;
  1498. X      }
  1499. X    }
  1500. X  } while (nextstat != 0);
  1501. X
  1502. X/* No entries found? print message and return FALSE */
  1503. X  if ((dircount + filecount) == 0)
  1504. X  {
  1505. X    if ((LSFlags & NOHEADERS) == 0)
  1506. X    {
  1507. X      if ((dfcount != 0) || ((LSFlags & WILDPATH) != 0))
  1508. X    WSTR("No match.\n");
  1509. X      else
  1510. X      {
  1511. X    LSFlagsX |= EMPTYDIRFLAG;
  1512. X
  1513. X    if ((LSFlags & LONGLIST) != 0)
  1514. X      WSTR("total 0\n");
  1515. X      }
  1516. X    }
  1517. X  }
  1518. X  else
  1519. X  {
  1520. X    if ((LSFlags & (SHOWDIRS | SHOWFILES)) != 0)
  1521. X    {
  1522. X      if ((LSFlags & LONGLIST) == 0)  /* Short listing wanted */
  1523. X      {
  1524. X    SListDir (fibhead);
  1525. X    gentrycnt++;
  1526. X      }
  1527. X      else                  /* Full listing */
  1528. X    LListDir (fibhead);
  1529. X
  1530. X      if ((dircount + filecount) > 0) gentrycnt++;
  1531. X    }
  1532. X  }
  1533. END_OF_FILE
  1534. if test 45212 -ne `wc -c <'src/ls.c.aa'`; then
  1535.     echo shar: \"'src/ls.c.aa'\" unpacked with wrong size!
  1536. fi
  1537. # end of 'src/ls.c.aa'
  1538. fi
  1539. echo shar: End of archive 4 \(of 4\).
  1540. cp /dev/null ark4isdone
  1541. MISSING=""
  1542. for I in 1 2 3 4 ; do
  1543.     if test ! -f ark${I}isdone ; then
  1544.     MISSING="${MISSING} ${I}"
  1545.     fi
  1546. done
  1547. if test "${MISSING}" = "" ; then
  1548.     echo You have unpacked all 4 archives.
  1549.     rm -f ark[1-9]isdone
  1550. else
  1551.     echo You still need to unpack the following archives:
  1552.     echo "        " ${MISSING}
  1553. fi
  1554. ##  End of shell archive.
  1555. exit 0
  1556. -- 
  1557. Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
  1558. Mail comments to the moderator at <amiga-request@cs.odu.edu>.
  1559. Post requests for sources, and general discussion to comp.sys.amiga.
  1560.